/*
 * Copyright 2011 Red Hat, Inc. and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package core.impl.p2;


import api.a1.definition.type.FactField;
import core.api.a1.TraitProxyClassBuilder;
import core.api.a3.CoreWrapper;
import core.api.a3.Thing;
import core.api.a3.Trait;
import core.api.a3.TraitFieldTMS;
import core.api.a3.TraitType;
import core.api.a3.TraitableBean;
import core.api.a3.Triple;
import core.api.a3.TripleFactory;
import core.impl.p1.TraitRegistry;
import core.impl.p1.TraitTypeMap;
import core.impl.p3.ClassDefinition;
import core.impl.p3.FieldDefinition;
import core.impl.p3.TraitProxy;
import core.impl.p5.TripleBasedBean;
import core.impl.p5.TripleBasedTypes;
import core.impl.p5.TripleStore;
import org.mvel2.MVEL;
import org.mvel2.asm.ClassVisitor;
import org.mvel2.asm.ClassWriter;
import org.mvel2.asm.FieldVisitor;
import org.mvel2.asm.Label;
import org.mvel2.asm.MethodVisitor;
import org.mvel2.asm.Opcodes;
import org.mvel2.asm.Type;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.BitSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import static core.impl.p3.ClassGenerator.createClassWriter;
import static core.impl.p5.BuildUtils.getInternalType;
import static core.impl.p5.BuildUtils.returnType;
import static core.impl.p5.BuildUtils.varType;
import static core.impl.p5.BuildUtils.*;
import static core.impl.p5.TraitBuilderUtil.MixinInfo;
import static core.impl.p5.TraitBuilderUtil.buildMixinMethods;
import static core.impl.p5.TraitBuilderUtil.findMixinInfo;
import static core.impl.p5.TraitBuilderUtil.getMixinName;

public class TraitTripleProxyClassBuilderImpl implements TraitProxyClassBuilder, Serializable {


    private transient ClassDefinition trait;

    private transient Class<?> proxyBaseClass;

    private transient TraitRegistry traitRegistry;

    protected ClassDefinition getTrait() {
        return trait;
    }

    public void init( ClassDefinition trait, Class<?> baseClass, TraitRegistry traitRegistry ) {
        this.trait = trait;
        this.proxyBaseClass = baseClass;
        this.traitRegistry = traitRegistry;
    }



    public byte[] buildClass( ClassDefinition core, ClassLoader classLoader ) throws IOException,
            SecurityException,
            IllegalArgumentException,
            ClassNotFoundException,
            NoSuchMethodException,
            IllegalAccessException,
            InvocationTargetException,
            InstantiationException,
            NoSuchFieldException {


        FieldVisitor fv;
        MethodVisitor mv;

        // get the method bitmask
        BitSet mask = traitRegistry.getFieldMask( getTrait().getName(), core.getDefinedClass().getName() );

        String name = TraitFactory.getPropertyWrapperName(getTrait(), core);
        String masterName = TraitFactory.getProxyName( getTrait(), core );
        Class<?> traitClass = getTrait().getDefinedClass();

        String internalWrapper  = getInternalType(name);
        String internalProxy = getInternalType(masterName);

        String internalCore     = Type.getInternalName(core.getDefinedClass());
        String descrCore        = Type.getDescriptor(core.getDefinedClass());
        String internalTrait    = Type.getInternalName(getTrait().getDefinedClass());

        MixinInfo mixinInfo = findMixinInfo( traitClass );

        ClassWriter cw = createClassWriter( classLoader,
                                            ACC_PUBLIC + ACC_SUPER,
                                            internalProxy,
                                            null,
                                            Type.getInternalName(proxyBaseClass),
                                            new String[] { internalTrait, Type.getInternalName(Externalizable.class) } );

        {
            fv = cw.visitField( ACC_PRIVATE + ACC_FINAL + ACC_STATIC,
                    TraitType.traitNameField, Type.getDescriptor(String.class),
                    null, null );
            fv.visitEnd();
        }
        {
            fv = cw.visitField( ACC_PUBLIC, "object", descrCore, null, null );
            fv.visitEnd();
        }
        {
            fv = cw.visitField( ACC_PRIVATE, "store", Type.getDescriptor(TripleStore.class), null, null );
            fv.visitEnd();
        }
        {
            fv = cw.visitField( ACC_PRIVATE, "storeId", Type.getDescriptor(String.class), null, null);
            fv.visitEnd();
        }

        if ( mixinInfo != null ) {
            for ( Class<?> mixinClass : mixinInfo.mixinClasses ) {
                {
                    fv = cw.visitField( ACC_PRIVATE,
                                        getMixinName(mixinClass),
                                        Type.getDescriptor(mixinClass),
                                        null, null );
                    fv.visitEnd();
                }
            }
        }

        {
            mv = cw.visitMethod( ACC_STATIC, "<clinit>", "()V", null, null );
            mv.visitCode();
            mv.visitLdcInsn( Type.getType(Type.getDescriptor(trait.getDefinedClass())) );
            mv.visitMethodInsn( INVOKEVIRTUAL,
                    Type.getInternalName(Class.class), "getName", "()" + Type.getDescriptor(String.class) );
            mv.visitFieldInsn( PUTSTATIC,
                    internalProxy,
                    TraitType.traitNameField,
                    Type.getDescriptor(String.class) );
            mv.visitInsn( RETURN );
            mv.visitMaxs( 0, 0 );
            mv.visitEnd();
        }

        {
            mv = cw.visitMethod( ACC_PUBLIC, "<init>", "()V", null, null );
            mv.visitCode();

            mv.visitVarInsn( ALOAD, 0 );
            mv.visitMethodInsn( INVOKESPECIAL, Type.getInternalName(proxyBaseClass), "<init>", "()V" );

            mv.visitInsn( RETURN );
//            mv.visitMaxs( 1, 1 );
            mv.visitMaxs( 0, 0 );
            mv.visitEnd();
        }

        {
            mv = cw.visitMethod( ACC_PUBLIC, "<init>", "(" + descrCore + Type.getDescriptor(TripleStore.class) + Type.getDescriptor(TripleFactory.class) + Type.getDescriptor(BitSet.class) + Type.getDescriptor(BitSet.class) + Type.getDescriptor(boolean.class) +")V", null, null );
            mv.visitCode();

            mv.visitVarInsn( ALOAD, 0 );
            mv.visitVarInsn( ALOAD, 2 );
            mv.visitMethodInsn( INVOKEVIRTUAL, Type.getInternalName(TripleStore.class), "getId", "()" + Type.getDescriptor(String.class) );
            mv.visitFieldInsn( PUTFIELD, internalProxy, "storeId", Type.getDescriptor(String.class) );

            int size = buildConstructorCore( cw, mv, internalProxy, internalWrapper, internalCore, descrCore, mixinInfo );

            initFields( mv, internalProxy );

            mv.visitInsn( RETURN );
//            mv.visitMaxs( 5 + size, 4 );
            mv.visitMaxs( 0, 0 );
            mv.visitEnd();
        }
        {
            mv = cw.visitMethod( ACC_PUBLIC, "_getTraitName", "()" + Type.getDescriptor(String.class), null, null);
            mv.visitCode();
            mv.visitFieldInsn( GETSTATIC, internalProxy, TraitType.traitNameField, Type.getDescriptor(String.class) );
            mv.visitInsn( ARETURN );
            mv.visitMaxs( 0, 0 );
            mv.visitEnd();
        }
        {
            mv = cw.visitMethod( ACC_PUBLIC, "getCore", "()" + descrCore + "", null, null );
            mv.visitCode();
            mv.visitVarInsn( ALOAD, 0 );
            mv.visitFieldInsn( GETFIELD, internalProxy, "object", descrCore );
            mv.visitInsn( ARETURN );
//            mv.visitMaxs( 1, 1 );
            mv.visitMaxs( 0, 0 );
            mv.visitEnd();
        }
        {
            mv = cw.visitMethod( ACC_PUBLIC, "getObject", "()" + Type.getDescriptor(TraitableBean.class), null, null );
            mv.visitCode();
            mv.visitVarInsn( ALOAD, 0 );
            mv.visitFieldInsn( GETFIELD, internalProxy, "object", descrCore );
            mv.visitTypeInsn( CHECKCAST, Type.getInternalName(TraitableBean.class) );
            mv.visitInsn( ARETURN );
//            mv.visitMaxs( 1, 1 );
            mv.visitMaxs( 0, 0 );
            mv.visitEnd();
        }
        {
            mv = cw.visitMethod(ACC_PUBLIC, "setObject", "(" + Type.getDescriptor(Object.class) + ")V", null, null);
            mv.visitCode();
            mv.visitVarInsn( ALOAD, 0 );
            mv.visitVarInsn( ALOAD, 1 );
            mv.visitTypeInsn( CHECKCAST, internalCore );
            mv.visitFieldInsn( PUTFIELD, internalProxy, "object", descrCore );
            mv.visitInsn( RETURN );
//            mv.visitMaxs( 2, 2 );
            mv.visitMaxs( 0, 0 );
            mv.visitEnd();
        }


        {
            mv = cw.visitMethod( ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "getCore", "()" + Type.getDescriptor(Object.class), null, null );
            mv.visitCode();
            mv.visitVarInsn( ALOAD, 0 );
            mv.visitMethodInsn( INVOKEVIRTUAL, internalProxy, "getCore", "()" + descrCore );
            mv.visitInsn( ARETURN );
//            mv.visitMaxs( 1, 1 );
            mv.visitMaxs( 0, 0 );
            mv.visitEnd();
        }

        {
            mv = cw.visitMethod( ACC_PUBLIC, "_isTop", "()Z", null, null );
            mv.visitCode();
            mv.visitInsn( Thing.class.equals( trait.getDefinedClass() ) ? ICONST_1 : ICONST_0 );
            mv.visitInsn( IRETURN );
            mv.visitMaxs( 0, 0 );
            mv.visitEnd();
        }

        {
            mv = cw.visitMethod( ACC_PUBLIC, "shed", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{}), null, null );
            mv.visitCode();

            if ( core.isFullTraiting() ) {
                Iterator<FieldDefinition> iter = trait.getFieldsDefinitions().iterator();
                for ( int j = 0; j < trait.getFieldsDefinitions().size(); j++ ) {
                    FieldDefinition fld = iter.next();
                    boolean hardField = ! TraitRegistry.isSoftField( fld, j, mask );
                    shedField( mv, fld, internalProxy, trait, core, hardField, j + 2 );
                }
            }

            mv.visitInsn( RETURN );
            mv.visitMaxs( 0, 0 );
            mv.visitEnd();
        }

        {
            mv = cw.visitMethod( ACC_PUBLIC, "writeExternal", "(" + Type.getDescriptor(ObjectOutput.class)+ ")V", null, new String[] { Type.getInternalName(IOException.class) } );
            mv.visitCode();

            mv.visitVarInsn( ALOAD, 0 );
            mv.visitVarInsn( ALOAD, 1 );
            mv.visitMethodInsn( INVOKESPECIAL, Type.getInternalName(proxyBaseClass), "writeExternal", "(" + Type.getDescriptor(ObjectOutput.class) + ")V" );


            mv.visitVarInsn( ALOAD, 1 );
            mv.visitVarInsn( ALOAD, 0 );
            mv.visitMethodInsn( INVOKEVIRTUAL, internalProxy, "getObject", "()" + Type.getDescriptor(TraitableBean.class) );
            mv.visitMethodInsn( INVOKEINTERFACE, Type.getInternalName(ObjectOutput.class), "writeObject", "(" + Type.getDescriptor(Object.class) + ")V" );


            mv.visitVarInsn( ALOAD, 1 );
            mv.visitVarInsn( ALOAD, 0 );
            mv.visitFieldInsn( GETFIELD, internalProxy, "storeId", Type.getDescriptor(String.class) );
            mv.visitMethodInsn( INVOKEINTERFACE, Type.getInternalName(ObjectOutput.class), "writeObject", "(" + Type.getDescriptor(Object.class) + ")V" );

            mv.visitVarInsn( ALOAD, 1 );
            mv.visitVarInsn( ALOAD, 0 );
            mv.visitFieldInsn( GETFIELD, internalProxy, "store", Type.getDescriptor(TripleStore.class) );
            mv.visitMethodInsn( INVOKEINTERFACE, Type.getInternalName(ObjectOutput.class), "writeObject", "(" + Type.getDescriptor(Object.class) + ")V" );



            mv.visitInsn( RETURN );
//            mv.visitMaxs( 2, 2 );
            mv.visitMaxs( 0, 0 );
            mv.visitEnd();
        }
        {
            mv = cw.visitMethod( ACC_PUBLIC, "readExternal", "(" + Type.getDescriptor(ObjectInput.class) + ")V", null,
                                 new String[] { Type.getInternalName(IOException.class), Type.getInternalName(ClassNotFoundException.class) } );
            mv.visitCode();

            mv.visitVarInsn( ALOAD, 0 );
            mv.visitVarInsn( ALOAD, 1 );
            mv.visitMethodInsn( INVOKESPECIAL, Type.getInternalName(proxyBaseClass), "readExternal", "(" + Type.getDescriptor(ObjectInput.class) + ")V" );


            mv.visitVarInsn( ALOAD, 0 );
            mv.visitVarInsn( ALOAD, 1 );
            mv.visitMethodInsn( INVOKEINTERFACE, Type.getInternalName(ObjectInput.class), "readObject", "()" + Type.getDescriptor(Object.class) );
            mv.visitTypeInsn( CHECKCAST, internalCore );
            mv.visitFieldInsn( PUTFIELD, internalProxy, "object", descrCore );


            mv.visitVarInsn( ALOAD, 0 );
            mv.visitVarInsn( ALOAD, 1 );
            mv.visitMethodInsn( INVOKEINTERFACE, Type.getInternalName(ObjectInput.class), "readObject", "()" + Type.getDescriptor(Object.class) );
            mv.visitTypeInsn( CHECKCAST, Type.getInternalName(String.class) );
            mv.visitFieldInsn( PUTFIELD, internalProxy, "storeId", Type.getDescriptor(String.class) );

            mv.visitVarInsn( ALOAD, 0 );
            mv.visitVarInsn( ALOAD, 1 );
            mv.visitMethodInsn( INVOKEINTERFACE, Type.getInternalName(ObjectInput.class), "readObject", "()" + Type.getDescriptor(Object.class) );
            mv.visitTypeInsn( CHECKCAST, Type.getInternalName(TripleStore.class) );
            mv.visitFieldInsn( PUTFIELD, internalProxy, "store", Type.getDescriptor(TripleStore.class) );


            mv.visitInsn( RETURN );
//            mv.visitMaxs( 3, 2 );
            mv.visitMaxs( 0, 0 );
            mv.visitEnd();
        }


        buildProxyAccessors( mask, cw, masterName, core, mixinInfo );

        boolean hasKeys = false;
        for ( FactField ff : getTrait().getFields() ) {
            if ( ff.isKey() ) {
                hasKeys = true;
                break;
            }
        }
        if ( ! hasKeys ) {
            buildEqualityMethods( cw, masterName, core.getClassName() );
        } else {
            buildKeyedEqualityMethods( cw, getTrait(), masterName, core.getClassName() );
        }

        buildMixinMethods(masterName, mixinInfo, cw);

        buildCommonMethods( cw, masterName );

        buildExtendedMethods( cw, getTrait(), core, mask );

        buildShadowMethods( cw, trait, core, mask );

        cw.visitEnd();

        return cw.toByteArray();

    }

    protected void buildShadowMethods( ClassWriter cw, ClassDefinition trait, ClassDefinition core, BitSet mask ) {
        for ( Method m : trait.getDefinedClass().getMethods() ) {
            if ( ! TraitFactory.excludeFromShadowing( m, trait ) ) {
                for ( Method q : core.getDefinedClass().getMethods() ) {
                    if ( TraitFactory.isCompatible( m, q ) ) {
                        buildShadowMethod( cw, trait, core, m, q );
                    }
                }
            }
        }
    }

    private void buildShadowMethod( ClassWriter cw, ClassDefinition trait, ClassDefinition core, Method m, Method q ) {
        MethodVisitor mv = cw.visitMethod( ACC_PUBLIC,
                                           m.getName(),
                                           Type.getMethodDescriptor(m),
                                           null,
                                           null );

        mv.visitCode();
        mv.visitVarInsn( ALOAD, 0 );
        mv.visitMethodInsn( INVOKEVIRTUAL,
                getInternalType(TraitFactory.getProxyName(trait, core)),
                            "getCore",
                            Type.getMethodDescriptor(Type.getType(core.getDefinedClass()), new Type[]{}),
                            false );

        for ( int j = 0; j < m.getParameterTypes().length; j++) {
            mv.visitVarInsn( varType(m.getParameterTypes()[j].getName()), j + 1 );
        }
        mv.visitMethodInsn( INVOKEVIRTUAL, Type.getInternalName(core.getDefinedClass()), m.getName(), Type.getMethodDescriptor(m), core.getDefinedClass().isInterface());

        mv.visitInsn( returnType(m.getReturnType().getName()) );

        mv.visitMaxs( 0, 0 );
        mv.visitEnd();

    }


    private boolean hasImpl( Trait annTrait ) {
        return annTrait != null && ! annTrait.impl().equals( Trait.NullMixin.class );
    }


    private <K extends Annotation> K getAnnotation( Class klass, Class<K> annotationClass ) {
        if ( klass.equals( Thing.class ) ) {
            return null;
        }
        K ann = (K) klass.getAnnotation( annotationClass );

        if ( ann == null ) {
            for ( Class sup : klass.getInterfaces() ) {
                ann = getAnnotation( sup, annotationClass );
                if ( ann != null ) {
                    return ann;
                }
            }
            return null;
        } else {
            return ann;
        }
    }


    protected int buildConstructorCore( ClassWriter cw, MethodVisitor mv, String internalProxy, String internalWrapper, String internalCore, String descrCore, MixinInfo mixinInfo ) {
        mv.visitVarInsn( ALOAD, 0 );
        mv.visitMethodInsn( INVOKESPECIAL, Type.getInternalName(proxyBaseClass), "<init>", "()V" );
        if (mixinInfo != null) {
            for ( Class<?> mixinClass : mixinInfo.mixinClasses ) {
                try {
                    //                    Constructor con = mixinClass.getConstructor( trait.getDefinedClass() );
                    Class actualArg = getPossibleConstructor( mixinClass, trait.getDefinedClass() );

                    mv.visitVarInsn( ALOAD, 0 );
                    mv.visitTypeInsn( NEW, Type.getInternalName(mixinClass) );
                    mv.visitInsn( DUP );
                    mv.visitVarInsn( ALOAD, 0 );
                    mv.visitMethodInsn( INVOKESPECIAL,
                                        Type.getInternalName(mixinClass),
                                        "<init>",
                                        "(" + Type.getDescriptor(actualArg) + ")V" );
                    mv.visitFieldInsn( PUTFIELD,
                                       internalProxy,
                                       getMixinName( mixinClass ),
                                       Type.getDescriptor(mixinClass) );
                } catch (NoSuchMethodException nsme) {
                    mv.visitVarInsn( ALOAD, 0 );
                    mv.visitTypeInsn( NEW, Type.getInternalName(mixinClass) );
                    mv.visitInsn( DUP );
                    mv.visitMethodInsn( INVOKESPECIAL, Type.getInternalName(mixinClass), "<init>", "()V" );
                    mv.visitFieldInsn( PUTFIELD,
                                       internalProxy,
                                       getMixinName( mixinClass ),
                                       Type.getDescriptor(mixinClass) );
                }
            }
        }

        mv.visitVarInsn( ALOAD, 0 );
        mv.visitVarInsn( ALOAD, 1 );
        mv.visitFieldInsn( PUTFIELD, internalProxy, "object", descrCore );

        mv.visitVarInsn( ALOAD, 0 );
        mv.visitVarInsn( ALOAD, 2 );
        mv.visitFieldInsn( PUTFIELD, internalProxy, "store", Type.getDescriptor(TripleStore.class) );

        mv.visitVarInsn( ALOAD, 0 );
        mv.visitVarInsn( ALOAD, 3 );
        mv.visitMethodInsn( INVOKEVIRTUAL, internalProxy, "setTripleFactory", "(" + Type.getDescriptor(TripleFactory.class) + ")V" );

        mv.visitVarInsn( ALOAD, 0 );
        mv.visitVarInsn( ALOAD, 4 );
        mv.visitMethodInsn( INVOKEVIRTUAL, internalProxy, "setTypeCode", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(BitSet.class)}) );

        mv.visitVarInsn( ALOAD, 0 );
        mv.visitTypeInsn( NEW, internalWrapper );
        mv.visitInsn( DUP );
        mv.visitVarInsn( ALOAD, 1 );
        mv.visitVarInsn( ALOAD, 2 );
        mv.visitVarInsn( ALOAD, 3 );
        mv.visitMethodInsn( INVOKESPECIAL, internalWrapper, "<init>", "(" + descrCore + Type.getDescriptor(TripleStore.class) + Type.getDescriptor(TripleFactory.class) + ")V" );
        mv.visitFieldInsn( PUTFIELD, internalProxy, "fields", Type.getDescriptor(Map.class) );


        mv.visitVarInsn( ALOAD, 1 );
        mv.visitMethodInsn( INVOKEVIRTUAL, internalCore, "_getDynamicProperties", "()" + Type.getDescriptor(Map.class) );
        Label l0 = new Label();
        mv.visitJumpInsn( IFNONNULL, l0 );

        mv.visitVarInsn( ALOAD, 1 );
        mv.visitTypeInsn( NEW, Type.getInternalName(TripleBasedBean.class) );
        mv.visitInsn( DUP );
        mv.visitVarInsn( ALOAD, 1 );
        mv.visitVarInsn( ALOAD, 2 );
        mv.visitVarInsn( ALOAD, 3 );
        mv.visitMethodInsn( INVOKESPECIAL, Type.getInternalName(TripleBasedBean.class), "<init>",
                            "(" + Type.getDescriptor(Object.class) + Type.getDescriptor(TripleStore.class) + Type.getDescriptor(TripleFactory.class) + ")V" );
        mv.visitMethodInsn( INVOKEVIRTUAL, internalCore, "_setDynamicProperties", "(" + Type.getDescriptor(Map.class) + ")V" );

        mv.visitLabel( l0 );



        mv.visitVarInsn( ALOAD, 1 );
        mv.visitMethodInsn( INVOKEVIRTUAL, internalCore, "_getTraitMap", "()" + Type.getDescriptor(Map.class) );
        Label l1 = new Label();
        mv.visitJumpInsn( IFNONNULL, l1 );

        mv.visitVarInsn( ALOAD, 1 );
        mv.visitTypeInsn( NEW, Type.getInternalName(TraitTypeMap.class) );
        mv.visitInsn( DUP );
        mv.visitTypeInsn( NEW, Type.getInternalName(TripleBasedTypes.class) );
        mv.visitInsn( DUP );
        mv.visitVarInsn( ALOAD, 1 );
        mv.visitVarInsn( ALOAD, 2 );
        mv.visitVarInsn( ALOAD, 3 );
        mv.visitMethodInsn( INVOKESPECIAL, Type.getInternalName(TripleBasedTypes.class), "<init>",
                            "(" + Type.getDescriptor(Object.class) + Type.getDescriptor(TripleStore.class) + Type.getDescriptor(TripleFactory.class) + ")V" );
        mv.visitMethodInsn( INVOKESPECIAL, Type.getInternalName(TraitTypeMap.class), "<init>", "(" + Type.getDescriptor(Map.class)+ ")V" );
        mv.visitMethodInsn( INVOKEVIRTUAL, internalCore, "_setTraitMap", "(" + Type.getDescriptor(Map.class) + ")V" );

        mv.visitLabel( l1 );


        mv.visitVarInsn( ALOAD, 1 );
        mv.visitVarInsn( ALOAD, 5 );
        mv.visitMethodInsn( INVOKEVIRTUAL, internalCore, "_setBottomTypeCode", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(BitSet.class)}) );

        // core.addTrait
        mv.visitVarInsn( ALOAD, 1 );
        mv.visitLdcInsn( trait.getName().endsWith( TraitFactory.SUFFIX ) ? trait.getName().replace(  TraitFactory.SUFFIX , "" ) : trait.getName() );
        mv.visitVarInsn( ALOAD, 0 );
        mv.visitMethodInsn( INVOKEVIRTUAL, internalCore, "addTrait",  Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(String.class), Type.getType(Thing.class)}) );

        return 3;
    }

    protected void initFields( MethodVisitor mv, String internalProxy ) {
        mv.visitVarInsn( ALOAD, 0 );
        mv.visitVarInsn( ILOAD, 6 );
        mv.visitMethodInsn( INVOKESPECIAL, internalProxy, "synchFields", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.BOOLEAN_TYPE}) );
    }

    private Class getPossibleConstructor(Class klass, Class arg) throws NoSuchMethodException {

        Constructor[] ctors = klass.getConstructors();

        for ( Constructor c : ctors ) {
            Class[] cpars = c.getParameterTypes();

            if ( cpars.length != 1 || ! cpars[0].isAssignableFrom( arg ) ) {
                continue;
            }

            return cpars[0];
        }
        throw new NoSuchMethodException( "Constructor for " + klass + " using " + arg + " not found " );
    }



    protected void buildProxyAccessors( BitSet mask, ClassWriter cw, String masterName, ClassDefinition core, MixinInfo mixinInfo) {
        int j = 0;

        for ( FieldDefinition field : getTrait().getFieldsDefinitions() ) {
            boolean isSoftField = TraitRegistry.isSoftField( field, j++, mask );
            buildProxyAccessor( mask, cw, masterName, core, mixinInfo, field, isSoftField );
        }

    }


    protected void buildProxyAccessor( BitSet mask, ClassWriter cw, String masterName, ClassDefinition core, MixinInfo mixinInfo, FieldDefinition field, boolean isSoftField ) {
        FieldVisitor fv;

        if ( core.isFullTraiting() ) {
            buildLogicalGetter( cw, field, masterName, trait, core );
            if ( ! isSoftField ) {
                buildHardSetter( cw, field, masterName, trait, core );
            } else {
                buildSoftSetter( cw, field, masterName, trait, core );
            }
        } else {
            if ( isSoftField ) {
                if (mixinInfo == null || !mixinInfo.isMixinGetter( field )) {
                    buildSoftGetter( cw, field, masterName, trait, core );
                    buildSoftSetter( cw, field, masterName, trait, core );
                }
            } else {
                buildHardGetter( cw, field, masterName, trait, core );
                buildHardSetter( cw, field, masterName, trait, core );
            }
        }
    }

    private void logicalSetter( MethodVisitor mv, FieldDefinition field, String masterName, ClassDefinition trait, ClassDefinition core, boolean hardField ) {
        String fieldType = field.getTypeName();
        int reg = 1 + sizeOf(fieldType);

        mv.visitVarInsn( ALOAD, 0 );
        mv.visitFieldInsn( GETFIELD, getInternalType(masterName), "object", Type.getDescriptor(core.getDefinedClass()) );
        mv.visitTypeInsn( CHECKCAST, Type.getInternalName(TraitableBean.class) );
        mv.visitMethodInsn( INVOKEINTERFACE, Type.getInternalName(TraitableBean.class), "_getFieldTMS", Type.getMethodDescriptor(Type.getType(TraitFieldTMS.class), new Type[]{}) );

        mv.visitVarInsn( ASTORE, reg );
        mv.visitVarInsn( ALOAD, reg );

        mv.visitLdcInsn( field.resolveAlias() );
        if ( isPrimitive(fieldType) ) {
            mv.visitVarInsn( varType(fieldType), 1 );
            mv.visitMethodInsn( Opcodes.INVOKESTATIC,
                                getInternalType(box(fieldType)),
                                "valueOf",
                                Type.getMethodDescriptor(Type.getType(getTypeDescriptor(box(fieldType))), new Type[]{Type.getType(getTypeDescriptor(fieldType))}) );
        } else {
            mv.visitVarInsn( ALOAD, 1 );
        }
        if ( isPrimitive(fieldType) ) {
            //            mv.visitFieldInsn( GETSTATIC, BuildUtils.getInternalType( BuildUtils.box( fieldType ) ), "TYPE", Type.getDescriptor( Class.class ) );
            mv.visitLdcInsn( Type.getType(getTypeDescriptor(box(field.getTypeName()))) );
        } else {
            mv.visitLdcInsn( Type.getType(Type.getDescriptor(field.getType())) );
        }
        mv.visitMethodInsn( INVOKEINTERFACE,
                            Type.getInternalName(TraitFieldTMS.class),
                            "set",
                            Type.getMethodDescriptor(Type.getType(Object.class), new Type[]{
                                    Type.getType(String.class), Type.getType(Object.class), Type.getType(Class.class)
                            }) );

        mv.visitVarInsn( ASTORE, 1 );
        mv.visitVarInsn( ALOAD, 1 );

        if ( isPrimitive(fieldType) ) {

            Label l0 = new Label();
            mv.visitJumpInsn( IFNULL, l0 );
            mv.visitVarInsn( ALOAD, 1 );
            Label l1 = new Label();
            mv.visitJumpInsn( GOTO, l1 );
            mv.visitLabel( l0 );
            mv.visitInsn( zero(fieldType) );
            mv.visitMethodInsn( Opcodes.INVOKESTATIC,
                                getInternalType(box(fieldType)),
                                "valueOf",
                                Type.getMethodDescriptor(Type.getType(getTypeDescriptor(box(fieldType))), new Type[]{Type.getType(getTypeDescriptor(fieldType))}) );
            mv.visitLabel( l1 );

            mv.visitTypeInsn( CHECKCAST, getInternalType(box(fieldType)) );
            mv.visitMethodInsn( INVOKEVIRTUAL,
                                getInternalType(box(fieldType)),
                                numericMorph(box(fieldType)),
                                Type.getMethodDescriptor(Type.getType(field.getType()), new Type[]{}) );
            mv.visitVarInsn( storeType(fieldType), 1 );
        }

    }


    private void buildLogicalGetter( ClassVisitor cw, FieldDefinition field, String proxy, ClassDefinition trait, ClassDefinition core ) {
        String fieldName = field.getName();
        String fieldType = field.getTypeName();

        String getter = getterName(fieldName, fieldType);

        MethodVisitor mv = cw.visitMethod( ACC_PUBLIC, getter, "()" + getTypeDescriptor(fieldType), null, null );
        mv.visitCode();

        mv.visitVarInsn( ALOAD, 0 );
        mv.visitFieldInsn( GETFIELD, getInternalType(proxy), "object", Type.getDescriptor(core.getDefinedClass()) );
        mv.visitTypeInsn( CHECKCAST, Type.getInternalName(TraitableBean.class) );
        mv.visitMethodInsn( INVOKEINTERFACE, Type.getInternalName(TraitableBean.class), "_getFieldTMS", Type.getMethodDescriptor(Type.getType(TraitFieldTMS.class), new Type[]{}) );

        mv.visitLdcInsn( field.resolveAlias() );
        if ( isPrimitive(fieldType) ) {
            //            mv.visitFieldInsn( GETSTATIC, BuildUtils.getInternalType( BuildUtils.box( fieldType ) ), "TYPE", Type.getDescriptor( Class.class ) );
            mv.visitLdcInsn( Type.getType(getTypeDescriptor(box(field.getTypeName()))) );
        } else {
            mv.visitLdcInsn( Type.getType(Type.getDescriptor(field.getType())) );
        }
        mv.visitMethodInsn( INVOKEINTERFACE,
                            Type.getInternalName(TraitFieldTMS.class),
                            "get",
                            Type.getMethodDescriptor(Type.getType(Object.class), new Type[]{Type.getType(String.class), Type.getType(Class.class)}) );

        mv.visitVarInsn( ASTORE, 1 );
        mv.visitVarInsn( ALOAD, 1 );

        if ( isPrimitive(fieldType) ) {
            Label l0 = new Label();
            mv.visitJumpInsn( IFNULL, l0 );
            mv.visitVarInsn( ALOAD, 1 );
            Label l1 = new Label();
            mv.visitJumpInsn( GOTO, l1 );
            mv.visitLabel( l0 );
            mv.visitInsn( zero(fieldType) );
            mv.visitMethodInsn( Opcodes.INVOKESTATIC,
                                getInternalType(box(fieldType)),
                                "valueOf",
                                Type.getMethodDescriptor(Type.getType(getTypeDescriptor(box(fieldType))), new Type[]{Type.getType(getTypeDescriptor(fieldType))}) );
            mv.visitLabel( l1 );

            mv.visitTypeInsn( CHECKCAST, getInternalType(box(fieldType)) );
            mv.visitMethodInsn( INVOKEVIRTUAL,
                                getInternalType(box(fieldType)),
                                numericMorph(box(fieldType)),
                                Type.getMethodDescriptor(Type.getType(field.getType()), new Type[]{}) );
            mv.visitInsn( returnType(fieldType) );
        } else {
            mv.visitTypeInsn( CHECKCAST, getInternalType(fieldType) );
            mv.visitInsn( ARETURN );
        }

        mv.visitMaxs( 0, 0 );
        mv.visitEnd();
    }





    protected void buildHardGetter( ClassVisitor cw, FieldDefinition field, String masterName, ClassDefinition proxy, ClassDefinition core ) {
        buildHardGetter( cw, field, masterName, proxy, core, getterName(field.getName(), field.getTypeName()), false );
    }

    protected void buildHardGetter( ClassVisitor cw, FieldDefinition field, String masterName, ClassDefinition proxy, ClassDefinition core, String getterName, boolean protect ) {
        Class fieldType = field.getType();


        MethodVisitor mv = cw.visitMethod( protect ? ACC_PROTECTED : ACC_PUBLIC,
                getterName,
                "()" + Type.getDescriptor(fieldType),
                null,
                null);
        mv.visitCode();

        TraitFactory.invokeExtractor( mv, masterName, proxy, core, field );

        if ( ! isPrimitive(field.getTypeName()) ) {
            mv.visitTypeInsn( CHECKCAST, Type.getInternalName(fieldType) );
        }

        mv.visitInsn( returnType(fieldType.getName()) );
//        mv.visitMaxs( 2, 1 );
        mv.visitMaxs( 0, 0 );
        mv.visitEnd();

    }



    protected void buildHardSetter( ClassVisitor cw, FieldDefinition field, String masterName, ClassDefinition trait, ClassDefinition core ) {
        buildHardSetter( cw, field, masterName, trait, core, setterName(field.getName(), field.getTypeName()), false );
    }

    protected void buildHardSetter( ClassVisitor cw, FieldDefinition field, String masterName, ClassDefinition trait, ClassDefinition core, String setterName, boolean protect ) {
        Class fieldType = field.getType();

        MethodVisitor mv = cw.visitMethod( protect ? ACC_PROTECTED : ACC_PUBLIC,
                                           setterName,
                                           "(" + Type.getDescriptor(field.getType()) + ")V",
                                           null,
                                           null );
        mv.visitCode();

        if ( core.isFullTraiting() ) {
            // The trait field update will be done by the core setter. However, types may mismatch here
            FieldDefinition hardField = core.getFieldByAlias( field.resolveAlias() );
            boolean isHardField = field.getTypeName().equals( hardField.getTypeName() );
            if ( ! field.getType().isPrimitive() && ! isHardField ) {
                boolean isCoreTrait = hardField.getType().getAnnotation( Trait.class ) != null;
                boolean isTraitTrait = field.getType().getAnnotation( Trait.class ) != null;

                Label l0 = new Label();
                mv.visitVarInsn( ALOAD, 1 );
                mv.visitJumpInsn( IFNULL, l0 );
                if ( isCoreTrait && ! isTraitTrait ) {
                    mv.visitVarInsn( ALOAD, 1 );
                    mv.visitTypeInsn( CHECKCAST, Type.getInternalName(TraitableBean.class) );
                    mv.visitLdcInsn( hardField.getTypeName() );
                    mv.visitMethodInsn( INVOKEINTERFACE, Type.getInternalName(TraitableBean.class), "getTrait", Type.getMethodDescriptor(Type.getType(Thing.class), new Type[]{Type.getType(String.class)}) );
                    mv.visitVarInsn( ASTORE, 1 );
                } else if ( ! isCoreTrait && isTraitTrait ) {
                    mv.visitVarInsn( ALOAD, 1 );
                    mv.visitTypeInsn( CHECKCAST, Type.getInternalName(TraitProxy.class) );
                    mv.visitMethodInsn( INVOKEVIRTUAL, Type.getInternalName(TraitProxy.class), "getObject", Type.getMethodDescriptor(Type.getType(TraitableBean.class), new Type[]{}) );
                    mv.visitVarInsn( ASTORE, 1 );
                } else if ( isCoreTrait && isTraitTrait ) {
                    mv.visitVarInsn( ALOAD, 1 );
                    mv.visitTypeInsn( CHECKCAST, Type.getInternalName(TraitProxy.class) );
                    mv.visitMethodInsn( INVOKEVIRTUAL, Type.getInternalName(TraitProxy.class), "getObject", Type.getMethodDescriptor(Type.getType(TraitableBean.class), new Type[]{}) );
                    mv.visitTypeInsn( CHECKCAST, Type.getInternalName(TraitableBean.class) );
                    mv.visitLdcInsn( hardField.getTypeName() );
                    mv.visitMethodInsn( INVOKEINTERFACE, Type.getInternalName(TraitableBean.class), "getTrait", Type.getMethodDescriptor(Type.getType(Thing.class), new Type[]{Type.getType(String.class)}) );
                    mv.visitVarInsn( ASTORE, 1 );
                } else {
                    // handled by normal inheritance, exceptions should have been thrown
                    if ( ! hardField.getType().isAssignableFrom( field.getType() ) ) {
                        mv.visitInsn( RETURN );
                    }
                }
                Label l1 = new Label();
                mv.visitJumpInsn(GOTO, l1);

                mv.visitLabel( l0 );
                mv.visitInsn( ACONST_NULL );
                mv.visitVarInsn( ASTORE, 1 );
                mv.visitLabel( l1 );
            } else if ( field.getType().isPrimitive() ) {
                if ( ! hardField.getType().equals( field.getType() ) ) {
                    mv.visitInsn( RETURN );
                }
            }

            if ( isHardField && CoreWrapper.class.isAssignableFrom( core.getDefinedClass() ) ) {
                logicalSetter( mv, field, masterName, this.trait, core, true );
            }
        }

        TraitFactory.invokeInjector( mv, masterName, trait, core, field, false, 1 );

        mv.visitInsn( RETURN );
        mv.visitMaxs( 0, 0 );
        mv.visitEnd();

    }




    protected void buildSoftSetter( ClassVisitor cw, FieldDefinition field, String proxy, ClassDefinition trait, ClassDefinition core ) {
        buildSoftSetter( cw, field, proxy, trait, core, setterName(field.getName(), field.getTypeName()), false );
    }

    protected void buildSoftSetter( ClassVisitor cw, FieldDefinition field, String proxy, ClassDefinition trait, ClassDefinition core, String setterName, boolean protect ) {
        String fieldName = field.getName();
        String type = field.getTypeName();

        MethodVisitor mv = cw.visitMethod( protect ? ACC_PROTECTED : ACC_PUBLIC,
                setterName,
                "(" + Type.getDescriptor(field.getType()) + ")V",
                null,
                null );
        mv.visitCode();

        if ( core.isFullTraiting() ) {
            logicalSetter( mv, field, proxy, this.trait, core, true );
        }

        mv.visitVarInsn( ALOAD, 0 );
        mv.visitFieldInsn( GETFIELD, getInternalType(proxy), "store", Type.getDescriptor(TripleStore.class) );
        mv.visitVarInsn( ALOAD, 0 );
        mv.visitLdcInsn( field.resolveAlias() );
        mv.visitVarInsn( varType(type), 1 );
        if ( isPrimitive(type) ) {
            TraitFactory.valueOf( mv, type );
        }
        mv.visitMethodInsn( INVOKEVIRTUAL, getInternalType(proxy), "property",
                            "(" + Type.getDescriptor(String.class) + Type.getDescriptor(Object.class) + ")" + Type.getDescriptor(Triple.class) );
        mv.visitMethodInsn( INVOKEVIRTUAL, Type.getInternalName(TripleStore.class), "put", "(" + Type.getDescriptor(Triple.class)+ ")Z" );

        mv.visitInsn( POP );
        mv.visitInsn( RETURN );
//        mv.visitMaxs( 3 + BuildUtils.sizeOf( type ), 1 + BuildUtils.sizeOf( type ) );
        mv.visitMaxs( 0, 0 );
        mv.visitEnd();

    }


    protected void buildSoftGetter( ClassVisitor cw, FieldDefinition field, String proxy, ClassDefinition trait, ClassDefinition core ) {
        buildSoftGetter( cw, field, proxy, trait, core, getterName(field.getName(), field.getTypeName()), false );
    }

    protected void buildSoftGetter( ClassVisitor cw, FieldDefinition field, String proxy, ClassDefinition trait, ClassDefinition core, String getterName, boolean protect ) {
        String fieldName = field.getName();
        String type = field.getTypeName();

        MethodVisitor mv = cw.visitMethod( protect ? ACC_PROTECTED : ACC_PUBLIC,
                getterName,
                "()"+ Type.getDescriptor(field.getType()),
                null,
                null );
        mv.visitCode();
        mv.visitVarInsn( ALOAD, 0 );
        mv.visitFieldInsn( GETFIELD, getInternalType(proxy), "store", Type.getDescriptor(TripleStore.class) );
        mv.visitVarInsn( ALOAD, 0 );
        mv.visitLdcInsn( field.resolveAlias() );

        mv.visitMethodInsn( INVOKEVIRTUAL, getInternalType(proxy), "propertyKey",
                            "(" + Type.getDescriptor(String.class) + ")" + Type.getDescriptor(Triple.class) );
        mv.visitMethodInsn( INVOKEVIRTUAL, Type.getInternalName(TripleStore.class), "get",
                            "(" + Type.getDescriptor(Triple.class) + ")" + Type.getDescriptor(Triple.class) );

        String actualType = isPrimitive(type) ? box(type) : type;

        mv.visitVarInsn( ASTORE, 1 );
        mv.visitVarInsn( ALOAD, 1 );
        Label l0 = new Label();
        mv.visitJumpInsn( IFNULL, l0 );
        mv.visitVarInsn( ALOAD, 1 );
        mv.visitMethodInsn( INVOKEINTERFACE, Type.getInternalName(Triple.class), "getValue", "()" + Type.getDescriptor(Object.class) );
        mv.visitVarInsn( ASTORE, 2 );
        mv.visitVarInsn( ALOAD, 2 );
        mv.visitTypeInsn( INSTANCEOF, getInternalType(actualType) );
        Label l1 = new Label();
        mv.visitJumpInsn( IFEQ, l1 );
        mv.visitVarInsn( ALOAD, 2 );
        mv.visitTypeInsn( CHECKCAST, getInternalType(actualType) );

        if ( isPrimitive(type) ) {
            TraitFactory.primitiveValue( mv, type );
            mv.visitInsn( returnType(type) );
            mv.visitLabel( l1 );
            mv.visitInsn( zero(type) );
            mv.visitInsn( returnType(type) );
            mv.visitLabel( l0 );
            mv.visitInsn( zero(type) );
            mv.visitInsn( returnType(type) );
        } else {
            mv.visitInsn( ARETURN );
            mv.visitLabel( l1 );
            mv.visitInsn( ACONST_NULL );
            mv.visitInsn( ARETURN );
            mv.visitLabel( l0 );
            mv.visitInsn( ACONST_NULL );
            mv.visitInsn( ARETURN );
        }
//        mv.visitMaxs( 3, 2 );
        mv.visitMaxs( 0, 0 );

        mv.visitEnd();
    }





    public void buildEqualityMethods( ClassVisitor cw, String proxy, String core ) {

    }






    public void buildKeyedEqualityMethods( ClassVisitor cw, ClassDefinition trait, String proxy, String core ) {
        String proxyType = getInternalType(proxy);

        buildKeyedEquals( cw, trait, proxyType );
        buildKeyedHashCode( cw, trait, proxyType );
    }


    protected void buildKeyedEquals( ClassVisitor cw,
                                     ClassDefinition classDef,
                                     String proxyType ) {
        MethodVisitor mv;
        mv = cw.visitMethod( ACC_PUBLIC, "equals", "(" + Type.getDescriptor(Object.class) + ")Z", null, null );
        mv.visitCode();

        mv.visitVarInsn( ALOAD, 0 );
        mv.visitVarInsn( ALOAD, 1 );
        Label l0 = new Label();
        mv.visitJumpInsn( IF_ACMPNE, l0 );
        mv.visitInsn( ICONST_1 );
        mv.visitInsn( IRETURN );

        mv.visitLabel( l0 );
        mv.visitVarInsn( ALOAD, 1 );
        Label l1 = new Label();
        mv.visitJumpInsn( IFNULL, l1 );
        mv.visitVarInsn( ALOAD, 0 );
        mv.visitMethodInsn( INVOKEVIRTUAL, Type.getInternalName(Object.class), "getClass", "()" + Type.getDescriptor(Class.class) );
        mv.visitVarInsn( ALOAD, 1 );
        mv.visitMethodInsn( INVOKEVIRTUAL, Type.getInternalName(Object.class), "getClass", "()" + Type.getDescriptor(Class.class) );
        Label l2 = new Label();
        mv.visitJumpInsn( IF_ACMPEQ, l2 );
        mv.visitLabel( l1 );
        mv.visitInsn( ICONST_0 );
        mv.visitInsn( IRETURN );
        mv.visitLabel( l2 );


        mv.visitVarInsn( ALOAD, 1 );
        mv.visitTypeInsn( CHECKCAST, proxyType );
        mv.visitVarInsn( ASTORE, 2 );

        int x = 2;

        int count = 0;

        for ( FieldDefinition field : classDef.getFieldsDefinitions() ) {
            if ( field.isKey() ) {
                count++;


                if ( ! isPrimitive(field.getTypeName()) ) {

                    mv.visitVarInsn( ALOAD, 0 );
                    mv.visitMethodInsn( INVOKEVIRTUAL, proxyType, getterName(field.getName(), field.getTypeName()),
                                        "()" + getTypeDescriptor(field.getTypeName()) );
                    Label l11 = new Label();
                    mv.visitJumpInsn( IFNULL, l11 );
                    mv.visitVarInsn( ALOAD, 0 );
                    mv.visitMethodInsn( INVOKEVIRTUAL, proxyType, getterName(field.getName(), field.getTypeName()),
                                        "()" + getTypeDescriptor(field.getTypeName()) );
                    mv.visitVarInsn( ALOAD, 2 );
                    mv.visitMethodInsn( INVOKEVIRTUAL, proxyType, getterName(field.getName(), field.getTypeName()),
                                        "()" + getTypeDescriptor(field.getTypeName()) );
                    mv.visitMethodInsn( INVOKEVIRTUAL, getInternalType(field.getTypeName()), "equals",
                                        "(" + Type.getDescriptor(Object.class) + ")Z" );
                    Label l12 = new Label();
                    mv.visitJumpInsn( IFNE, l12 );
                    Label l13 = new Label();
                    mv.visitJumpInsn( GOTO, l13 );
                    mv.visitLabel( l11 );
                    mv.visitVarInsn( ALOAD, 2 );
                    mv.visitMethodInsn( INVOKEVIRTUAL, proxyType, getterName(field.getName(), field.getTypeName()),
                                        "()" + getTypeDescriptor(field.getTypeName()) );
                    mv.visitJumpInsn( IFNULL, l12 );
                    mv.visitLabel( l13 );
                    mv.visitInsn( ICONST_0 );
                    mv.visitInsn( IRETURN );
                    mv.visitLabel( l12 );

                } else if ( "double".equals( field.getTypeName() ) ) {

                    mv.visitVarInsn( ALOAD, 2 );
                    mv.visitMethodInsn( INVOKEVIRTUAL, proxyType, getterName(field.getName(), field.getTypeName()),
                                        "()" + getTypeDescriptor(field.getTypeName()) );
                    mv.visitVarInsn( ALOAD, 0 );
                    mv.visitMethodInsn( INVOKEVIRTUAL, proxyType, getterName(field.getName(), field.getTypeName()),
                                        "()" + getTypeDescriptor(field.getTypeName()) );
                    mv.visitMethodInsn( INVOKESTATIC, Type.getInternalName(Double.class), "compare", "(DD)I" );
                    Label l5 = new Label();
                    mv.visitJumpInsn( IFEQ, l5 );
                    mv.visitInsn( ICONST_0 );
                    mv.visitInsn( IRETURN );
                    mv.visitLabel( l5 );

                    x = Math.max( x, 4 );

                } else if ( "float".equals( field.getTypeName() ) ) {

                    mv.visitVarInsn( ALOAD, 2 );
                    mv.visitMethodInsn( INVOKEVIRTUAL, proxyType, getterName(field.getName(), field.getTypeName()),
                                        "()" + getTypeDescriptor(field.getTypeName()) );
                    mv.visitVarInsn( ALOAD, 0 );
                    mv.visitMethodInsn( INVOKEVIRTUAL, proxyType, getterName(field.getName(), field.getTypeName()),
                                        "()" + getTypeDescriptor(field.getTypeName()) );
                    mv.visitMethodInsn( INVOKESTATIC, Type.getInternalName(Float.class), "compare", "(FF)I" );
                    Label l6 = new Label();
                    mv.visitJumpInsn( IFEQ, l6 );
                    mv.visitInsn( ICONST_0 );
                    mv.visitInsn( IRETURN );
                    mv.visitLabel( l6 );


                }  else if ( "long".equals( field.getTypeName() ) ) {

                    mv.visitVarInsn( ALOAD, 0 );
                    mv.visitMethodInsn( INVOKEVIRTUAL, proxyType, getterName(field.getName(), field.getTypeName()),
                                        "()" + getTypeDescriptor(field.getTypeName()) );
                    mv.visitVarInsn( ALOAD, 2 );
                    mv.visitMethodInsn( INVOKEVIRTUAL, proxyType, getterName(field.getName(), field.getTypeName()),
                                        "()" + getTypeDescriptor(field.getTypeName()) );
                    mv.visitInsn( LCMP );
                    Label l8 = new Label();
                    mv.visitJumpInsn( IFEQ, l8 );
                    mv.visitInsn( ICONST_0 );
                    mv.visitInsn( IRETURN );
                    mv.visitLabel( l8 );

                    x = Math.max( x, 4 );

                } else {

                    mv.visitVarInsn( ALOAD, 0 );
                    mv.visitMethodInsn( INVOKEVIRTUAL, proxyType, getterName(field.getName(), field.getTypeName()),
                                        "()" + getTypeDescriptor(field.getTypeName()) );
                    mv.visitVarInsn( ALOAD, 2 );
                    mv.visitMethodInsn( INVOKEVIRTUAL, proxyType, getterName(field.getName(), field.getTypeName()),
                                        "()" + getTypeDescriptor(field.getTypeName()) );
                    Label l4 = new Label();
                    mv.visitJumpInsn( IF_ICMPEQ, l4 );
                    mv.visitInsn( ICONST_0 );
                    mv.visitInsn( IRETURN );
                    mv.visitLabel( l4 );

                }

            }
        }

        mv.visitInsn( ICONST_1 );
        mv.visitInsn( IRETURN );
//        mv.visitMaxs( x, 3 );
        mv.visitMaxs( 0, 0 );
        mv.visitEnd();
    }

    protected void buildKeyedHashCode( ClassVisitor cw,
                                       ClassDefinition classDef,
                                       String proxyType ) {

        MethodVisitor mv;

        {
            mv = cw.visitMethod( ACC_PUBLIC, "hashCode", "()I", null, null );
            mv.visitCode();
            mv.visitIntInsn( BIPUSH, 31 );
            mv.visitVarInsn( ISTORE, 1 );

            int count = 0;
            int x = 2;
            int y = 2;
            for ( FieldDefinition field : classDef.getFieldsDefinitions() ) {
                if ( field.isKey() ) {
                    count++;

                    if ( ! isPrimitive(field.getTypeName()) ) {

                        mv.visitIntInsn( BIPUSH, 31 );
                        mv.visitVarInsn( ILOAD, 1 );
                        mv.visitInsn( IMUL );
                        mv.visitVarInsn( ALOAD, 0 );
                        mv.visitMethodInsn( INVOKEVIRTUAL, proxyType, getterName(field.getName(), field.getTypeName()),
                                            "()" + getTypeDescriptor(field.getTypeName()) );
                        Label l8 = new Label();
                        mv.visitJumpInsn( IFNULL, l8 );
                        mv.visitVarInsn( ALOAD, 0 );
                        mv.visitMethodInsn( INVOKEVIRTUAL, proxyType, getterName(field.getName(), field.getTypeName()),
                                            "()" + getTypeDescriptor(field.getTypeName()) );
                        mv.visitMethodInsn( INVOKEVIRTUAL, getInternalType(field.getTypeName()), "hashCode", "()I" );
                        Label l9 = new Label();
                        mv.visitJumpInsn( GOTO, l9 );
                        mv.visitLabel( l8 );
                        mv.visitInsn( ICONST_0 );
                        mv.visitLabel( l9 );
                        mv.visitInsn( IADD );
                        mv.visitVarInsn( ISTORE, 1 );

                    } else if ( "double".equals( field.getTypeName() ) ) {

                        mv.visitVarInsn( ALOAD, 0 );
                        mv.visitMethodInsn( INVOKEVIRTUAL, proxyType, getterName(field.getName(), field.getTypeName()),
                                            "()" + getTypeDescriptor(field.getTypeName()) );
                        mv.visitInsn( DCONST_0 );
                        mv.visitInsn( DCMPL );
                        Label l2 = new Label();
                        mv.visitJumpInsn( IFEQ, l2 );
                        mv.visitVarInsn( ALOAD, 0 );
                        mv.visitMethodInsn( INVOKEVIRTUAL, proxyType, getterName(field.getName(), field.getTypeName()),
                                            "()" + getTypeDescriptor(field.getTypeName()) );
                        mv.visitMethodInsn( INVOKESTATIC, Type.getInternalName(Double.class), "doubleToLongBits", "(D)J" );
                        Label l3 = new Label();
                        mv.visitJumpInsn( GOTO, l3 );
                        mv.visitLabel( l2 );
                        mv.visitInsn( LCONST_0 );
                        mv.visitLabel( l3 );
                        mv.visitVarInsn( LSTORE, 2 );
                        mv.visitIntInsn( BIPUSH, 31 );
                        mv.visitVarInsn( ILOAD, 1 );
                        mv.visitInsn( IMUL );
                        mv.visitVarInsn( LLOAD, 2 );
                        mv.visitVarInsn( LLOAD, 2 );
                        mv.visitIntInsn( BIPUSH, 32 );
                        mv.visitInsn( LUSHR );
                        mv.visitInsn( LXOR );
                        mv.visitInsn( L2I );
                        mv.visitInsn( IADD);
                        mv.visitVarInsn( ISTORE, 1 );

                        x = Math.max( 6, x );
                        y = Math.max( 4, y );

                    } else if ( "boolean".equals( field.getTypeName() ) ) {

                        mv.visitIntInsn( BIPUSH, 31 );
                        mv.visitVarInsn( ILOAD, 1 );
                        mv.visitInsn( IMUL );
                        mv.visitVarInsn( ALOAD, 0 );
                        mv.visitMethodInsn( INVOKEVIRTUAL, proxyType, getterName(field.getName(), field.getTypeName()),
                                            "()" + getTypeDescriptor(field.getTypeName()) );
                        Label l4 = new Label();
                        mv.visitJumpInsn( IFEQ, l4 );
                        mv.visitInsn( ICONST_1 );
                        Label l5 = new Label();
                        mv.visitJumpInsn( GOTO, l5 );
                        mv.visitLabel( l4 );
                        mv.visitInsn( ICONST_0 );
                        mv.visitLabel( l5 );
                        mv.visitInsn( IADD );
                        mv.visitVarInsn( ISTORE, 1 );

                    } else if ( "float".equals( field.getTypeName() ) ) {

                        mv.visitIntInsn( BIPUSH, 31 );
                        mv.visitVarInsn( ILOAD, 1 );
                        mv.visitInsn( IMUL );
                        mv.visitVarInsn( ALOAD, 0 );
                        mv.visitMethodInsn( INVOKEVIRTUAL, proxyType, getterName(field.getName(), field.getTypeName()),
                                            "()" + getTypeDescriptor(field.getTypeName()) );
                        mv.visitInsn( FCONST_0 );
                        mv.visitInsn( FCMPL );
                        Label l6 = new Label();
                        mv.visitJumpInsn( IFEQ, l6 );
                        mv.visitVarInsn( ALOAD, 0 );
                        mv.visitMethodInsn( INVOKEVIRTUAL, proxyType, getterName(field.getName(), field.getTypeName()),
                                            "()" + getTypeDescriptor(field.getTypeName()) );
                        mv.visitMethodInsn( INVOKESTATIC, Type.getInternalName(Float.class), "floatToIntBits", "(F)I" );
                        Label l7 = new Label();
                        mv.visitJumpInsn( GOTO, l7 );
                        mv.visitLabel( l6 );
                        mv.visitInsn( ICONST_0 );
                        mv.visitLabel( l7 );
                        mv.visitInsn( IADD );
                        mv.visitVarInsn( ISTORE, 1 );

                        x = Math.max( 3, x );

                    }  else if ( "long".equals( field.getTypeName() ) ) {

                        mv.visitIntInsn( BIPUSH, 31 );
                        mv.visitVarInsn( ILOAD, 1 );
                        mv.visitInsn( IMUL );
                        mv.visitVarInsn( ALOAD, 0 );
                        mv.visitMethodInsn( INVOKEVIRTUAL, proxyType, getterName(field.getName(), field.getTypeName()),
                                            "()" + getTypeDescriptor(field.getTypeName()) );
                        mv.visitVarInsn( ALOAD, 0 );
                        mv.visitMethodInsn( INVOKEVIRTUAL, proxyType, getterName(field.getName(), field.getTypeName()),
                                            "()" + getTypeDescriptor(field.getTypeName()) );
                        mv.visitIntInsn( BIPUSH, 32 );
                        mv.visitInsn( LUSHR );
                        mv.visitInsn( LXOR );
                        mv.visitInsn( L2I );
                        mv.visitInsn( IADD );
                        mv.visitVarInsn( ISTORE, 1 );

                        x = Math.max( 6, x );

                    } else {

                        mv.visitIntInsn( BIPUSH, 31 );
                        mv.visitVarInsn( ILOAD, 1 );
                        mv.visitInsn( IMUL );
                        mv.visitVarInsn( ALOAD, 0 );
                        mv.visitMethodInsn( INVOKEVIRTUAL, proxyType, getterName(field.getName(), field.getTypeName()),
                                            "()" + getTypeDescriptor(field.getTypeName()) ) ;
                        mv.visitInsn( IADD );
                        mv.visitVarInsn( ISTORE, 1 );

                    }
                }

            }
            mv.visitVarInsn( ILOAD, 1 );
            mv.visitInsn( IRETURN );
//            mv.visitMaxs( x, y );
            mv.visitMaxs( 0, 0 );
            mv.visitEnd();
        }
    }





    protected void buildCommonMethods( ClassWriter cw, String proxy ) {
        MethodVisitor mv;
        {
            mv = cw.visitMethod( ACC_PUBLIC, "toString", "()" + Type.getDescriptor(String.class), null, null );
            mv.visitCode();
            mv.visitTypeInsn( NEW, Type.getInternalName(StringBuilder.class) );
            mv.visitInsn( DUP );
            mv.visitMethodInsn( INVOKESPECIAL, Type.getInternalName(StringBuilder.class), "<init>", "()V" );
            mv.visitLdcInsn( "(@" + proxy + ") : " );
            mv.visitMethodInsn( INVOKEVIRTUAL, Type.getInternalName(StringBuilder.class), "append", "(" + Type.getDescriptor(String.class) + ")" + Type.getDescriptor(StringBuilder.class) );
            mv.visitVarInsn( ALOAD, 0 );
            mv.visitMethodInsn( INVOKEVIRTUAL, getInternalType(proxy), "getFields", "()" + Type.getDescriptor(Map.class) );
            mv.visitMethodInsn( INVOKEINTERFACE, Type.getInternalName(Map.class) , "entrySet", "()" + Type.getDescriptor(Set.class) );
            mv.visitMethodInsn( INVOKEVIRTUAL, Type.getInternalName(Object.class), "toString", "()" + Type.getDescriptor(String.class));
            mv.visitMethodInsn( INVOKEVIRTUAL, Type.getInternalName(StringBuilder.class), "append", "(" + Type.getDescriptor(String.class) + ")" + Type.getDescriptor(StringBuilder.class) );
            mv.visitMethodInsn( INVOKEVIRTUAL, Type.getInternalName(StringBuilder.class), "toString", "()" + Type.getDescriptor(String.class));
            mv.visitInsn( ARETURN );
//            mv.visitMaxs( 2, 1 );
            mv.visitMaxs( 0, 0 );
            mv.visitEnd();

        }

    }



    protected void buildExtendedMethods(ClassWriter cw, ClassDefinition trait, ClassDefinition core, BitSet mask ) {
        buildSynchFields( cw, TraitFactory.getProxyName( trait, core ), trait, core, mask );
    }

    protected void buildSynchFields( ClassWriter cw, String proxyName, ClassDefinition trait, ClassDefinition core, BitSet mask ) {
        {
            MethodVisitor mv = cw.visitMethod( ACC_PRIVATE, "synchFields", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.BOOLEAN_TYPE}), null, null );
            mv.visitCode();

            if ( core.isFullTraiting() ) {
                Iterator<FieldDefinition> iter = trait.getFieldsDefinitions().iterator();
                for ( int j = 0; j < trait.getFieldsDefinitions().size(); j++ ) {
                    FieldDefinition fld = iter.next();
                    boolean hardField = ! TraitRegistry.isSoftField( fld, j, mask );
                    synchFieldLogical( mv, fld, proxyName, trait, core, hardField, j + 3 );
                }
            } else {
                for ( FieldDefinition fld : trait.getFieldsDefinitions() ) {
                    if ( fld.getInitExpr() != null ) {
                        synchField( mv, fld, proxyName );
                    }
                }
            }

            mv.visitInsn( RETURN );
            mv.visitMaxs( 0, 0 );
            mv.visitEnd();
        }
    }


    protected void synchField( MethodVisitor mv, FieldDefinition fld, String proxyName ) {
        mv.visitVarInsn( ALOAD, 0 );
        mv.visitMethodInsn( INVOKEVIRTUAL,
                            getInternalType(proxyName),
                            getterName(fld.getName(), fld.getTypeName()),
                            "()" + getTypeDescriptor(fld.getTypeName()) );

        Label l0 = null;
        if ( ! isPrimitive(fld.getTypeName()) ) {
            l0 = new Label();
            mv.visitJumpInsn( IFNONNULL, l0 );
        }

        mv.visitVarInsn( ALOAD, 0 );
        mv.visitLdcInsn( fld.getInitExpr() );
        if ( isPrimitive(fld.getTypeName()) ) {
            mv.visitFieldInsn( GETSTATIC, getInternalType(box(fld.getTypeName())), "TYPE", Type.getDescriptor(Class.class) );
        } else {
            mv.visitLdcInsn( Type.getType(getTypeDescriptor(fld.getTypeName())) );
        }
        mv.visitMethodInsn( INVOKESTATIC,
                            Type.getInternalName(MVEL.class),
                            "eval",
                            Type.getMethodDescriptor(Type.getType(Object.class), new Type[]{Type.getType(String.class), Type.getType(Class.class)}) );
        if ( isPrimitive(fld.getTypeName()) ) {
            mv.visitTypeInsn( CHECKCAST, getInternalType(box(fld.getTypeName())) );
            mv.visitMethodInsn( INVOKEVIRTUAL,
                                getInternalType(box(fld.getTypeName())),
                                numericMorph(box(fld.getTypeName())),
                                "()" + getTypeDescriptor(fld.getTypeName()) );
        } else {
            mv.visitTypeInsn( CHECKCAST, getInternalType(fld.getTypeName()) );
        }
        mv.visitMethodInsn( INVOKEVIRTUAL,
                            getInternalType(proxyName),
                            setterName(fld.getName(), fld.getTypeName()),
                            "(" + getTypeDescriptor(fld.getTypeName()) + ")" + Type.getDescriptor(void.class) );
        if ( ! isPrimitive(fld.getTypeName()) ) {
            mv.visitLabel( l0 );
        }
    }



    protected void synchFieldLogical( MethodVisitor mv, FieldDefinition fld, String proxyName, ClassDefinition trait, ClassDefinition core, boolean hardField, int j ) {

        mv.visitVarInsn( ALOAD, 0 );
        mv.visitFieldInsn( GETFIELD, getInternalType(proxyName), "object", Type.getDescriptor(core.getDefinedClass()) );
        mv.visitTypeInsn( CHECKCAST, Type.getInternalName(TraitableBean.class) );
        mv.visitMethodInsn( INVOKEINTERFACE, Type.getInternalName(TraitableBean.class), "_getFieldTMS", Type.getMethodDescriptor(Type.getType(TraitFieldTMS.class), new Type[]{}) );
        mv.visitVarInsn( ASTORE, 2 );
        mv.visitVarInsn( ALOAD, 2 );
        // fld Name
        mv.visitLdcInsn( fld.resolveAlias() );
        // this
        mv.visitVarInsn( ALOAD, 0 );
        // init expr
        if ( fld.getInitExpr() != null ) {
            mv.visitLdcInsn( fld.getInitExpr() );
        } else {
            mv.visitInsn( ACONST_NULL );
        }
        // fld type
        if ( isPrimitive(fld.getTypeName()) ) {
//                mv.visitFieldInsn( GETSTATIC, BuildUtils.getInternalType( BuildUtils.box( fld.getTypeName() ) ), "TYPE", Type.getDescriptor( Class.class ) );
            mv.visitLdcInsn( Type.getType(getTypeDescriptor(box(fld.getTypeName()))) );
        } else {
            mv.visitLdcInsn( Type.getType(Type.getDescriptor(fld.getType())) );
        }
        mv.visitVarInsn( ILOAD, 1 );
        mv.visitMethodInsn( INVOKEINTERFACE,
                            Type.getInternalName(TraitFieldTMS.class),
                            "donField",
                            Type.getMethodDescriptor(Type.getType(Object.class), new Type[]{
                                    Type.getType(String.class), Type.getType(TraitType.class), Type.getType(String.class), Type.getType(Class.class), Type.BOOLEAN_TYPE
                            }) );

        mv.visitVarInsn( ASTORE, j );
        mv.visitVarInsn( ALOAD, 0 );
        mv.visitVarInsn( ALOAD, j );

        if ( isPrimitive(fld.getTypeName()) ) {
            Label l0 = new Label();
            mv.visitJumpInsn( IFNULL, l0 );
            mv.visitVarInsn( ALOAD, j );
            mv.visitTypeInsn( CHECKCAST, getInternalType(box(fld.getTypeName())) );
            mv.visitMethodInsn( INVOKEVIRTUAL,
                                getInternalType(box(fld.getTypeName())),
                                numericMorph(box(fld.getTypeName())),
                                Type.getMethodDescriptor(Type.getType(fld.getType()), new Type[]{}) );
            Label l1 = new Label();
            mv.visitJumpInsn( GOTO, l1 );
            mv.visitLabel( l0 );
            mv.visitInsn( zero(fld.getTypeName()) );
            mv.visitLabel( l1 );
        } else {
            mv.visitTypeInsn( CHECKCAST, Type.getInternalName(fld.getType()) );
        }

        mv.visitMethodInsn( INVOKEVIRTUAL,
                            getInternalType(proxyName),
                            setterName(fld.getName(), fld.getTypeName()),
                            "(" + getTypeDescriptor(fld.getTypeName()) + ")" + Type.getDescriptor(void.class) );
    }




    protected void shedField( MethodVisitor mv, FieldDefinition fld, String proxyName, ClassDefinition trait, ClassDefinition core, boolean hardField, int j ) {
        FieldDefinition coreField = core.getFieldByAlias( fld.resolveAlias() );

        mv.visitVarInsn( ALOAD, 0 );
        mv.visitFieldInsn( GETFIELD, getInternalType(proxyName), "object", Type.getDescriptor(core.getDefinedClass()) );
        mv.visitTypeInsn( CHECKCAST, Type.getInternalName(TraitableBean.class) );
        mv.visitMethodInsn( INVOKEINTERFACE, Type.getInternalName(TraitableBean.class), "_getFieldTMS", Type.getMethodDescriptor(Type.getType(TraitFieldTMS.class), new Type[]{}) );

        mv.visitVarInsn( ASTORE, 1 );
        mv.visitVarInsn( ALOAD, 1 );
        // fld Name
        mv.visitLdcInsn( fld.resolveAlias() );
        // this
        mv.visitVarInsn( ALOAD, 0 );
        // fld type
        if ( isPrimitive(fld.getTypeName()) ) {
            mv.visitLdcInsn( Type.getType(getTypeDescriptor(box(fld.getTypeName()))) );
        } else {
            mv.visitLdcInsn( Type.getType(Type.getDescriptor(fld.getType())) );
        }

        if ( hardField ) {
            if ( isPrimitive(coreField.getTypeName()) ) {
                mv.visitLdcInsn( Type.getType(getTypeDescriptor(box(coreField.getTypeName()))) );
            } else {
                mv.visitLdcInsn( Type.getType(Type.getDescriptor(coreField.getType())) );
            }
        } else {
            mv.visitLdcInsn( Type.getType(Type.getDescriptor(Object.class)) );
        }


        mv.visitMethodInsn( INVOKEINTERFACE,
                            Type.getInternalName(TraitFieldTMS.class),
                            "shedField",
                            Type.getMethodDescriptor(Type.getType(Object.class), new Type[]{
                                    Type.getType(String.class), Type.getType(TraitType.class), Type.getType(Class.class), Type.getType(Class.class)
                            }) );

        mv.visitVarInsn( ASTORE, j );

        if ( hardField ) {
            mv.visitVarInsn( ALOAD, 0 );
            mv.visitFieldInsn( GETFIELD, getInternalType(proxyName), "object", Type.getDescriptor(core.getDefinedClass()) );

            mv.visitVarInsn( ALOAD, j );
            if ( isPrimitive(coreField.getTypeName()) ) {
                Label l0 = new Label();
                mv.visitJumpInsn( IFNULL, l0 );
                mv.visitVarInsn( ALOAD, j );
                mv.visitTypeInsn( CHECKCAST, getInternalType(box(coreField.getTypeName())) );
                mv.visitMethodInsn( INVOKEVIRTUAL,
                                    getInternalType(box(coreField.getTypeName())),
                                    numericMorph(box(coreField.getTypeName())),
                                    Type.getMethodDescriptor(Type.getType(coreField.getType()), new Type[]{}) );
                Label l1 = new Label();
                mv.visitJumpInsn( GOTO, l1 );
                mv.visitLabel( l0 );
                mv.visitInsn( zero(coreField.getTypeName()) );
                mv.visitLabel( l1 );
            } else {
                mv.visitTypeInsn( CHECKCAST, Type.getInternalName(coreField.getType()) );
            }

            mv.visitMethodInsn( INVOKEVIRTUAL,
                                Type.getInternalName(core.getDefinedClass()),
                                setterName(coreField.getName(), coreField.getTypeName()),
                                "(" + getTypeDescriptor(coreField.getTypeName()) + ")" + Type.getDescriptor(void.class) );
        } else {
            mv.visitVarInsn( ALOAD, 0 );
            mv.visitFieldInsn( GETFIELD, getInternalType(proxyName), "map", Type.getDescriptor(Map.class) );

            mv.visitLdcInsn( fld.resolveAlias() );
            mv.visitVarInsn( ALOAD, j );

            mv.visitMethodInsn( INVOKEINTERFACE,
                                Type.getInternalName(Map.class),
                                "put",
                                Type.getMethodDescriptor(Type.getType(Object.class), new Type[]{Type.getType(Object.class), Type.getType(Object.class)}) );
        }
    }



}